﻿#undef DEBUG
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.Serialization;
using System.Xml.Serialization;
using System.Xml;
using System.IO;

namespace AlphabetsToMatrix
{
    public class Program
    {
        public struct Point
        {
            public int X;
            public int Y;
            public Point(int x, int y)
            {
                X = x;
                Y = y;
            }
        }
        public struct Rectangle
        {
            public int X;
            public int Y;
            public int Height;
            public int Width;
            public Rectangle(int x, int y, int width, int height)
            {
                X = x;
                Y = y;
                Height = height;
                Width = width;
            }
            public Rectangle(Point first, Point second)
                : this(first.X, first.Y, Math.Abs(second.X - first.X), Math.Abs(second.Y - first.Y))
            {

            }
            public Rectangle(Point first, int width, int height)
                : this(first.X, first.Y, width, height)
            {

            }
        }
        [Serializable]
        public class CharacterData : IXmlSerializable
        {

            public double[] value
            {
                get;
                private set;
            }
            [NonSerialized]
            System.Drawing.Bitmap image;
            char alphabet;
            private CharacterData() { }
            public CharacterData(System.Drawing.Bitmap image, char alphabet)
            {
                this.image = image;
                this.alphabet = alphabet;
                
            }

            public double[] CalculateAlphabetMatrix()
            {
                int scale = (int)Math.Ceiling((double)image.Width / 5);
                int X = 0;
                int Y = 0;
                int j = 0;
                System.Drawing.Color c;
                double[] result = new double[image.Height / scale * image.Width / scale];
                for (int i = 1; i <= result.Length; i++)
                {
                    result[i - 1] = 0;
                    double sum = 0;
                    int CounterX = 0;
                    int CounterY = 0;
                    while (CounterY < scale)
                    {
                        if (X + CounterX < image.Width && Y + CounterY < image.Height)
                        {
                            c = image.GetPixel(X + CounterX, Y + CounterY);
                            sum += (c.R + c.B + c.G) / 3;
                        }
                        CounterX++;
                        if (CounterX >= scale)
                        {
                            CounterX = 0;
                            CounterY++;
                        }

                    }
                    result[i - 1] = sum / (scale * scale);
                    X += scale;
                    if (X >= image.Width)
                    {
                        Y += scale;
                        X = 0;
                    }
                }
                this.value = result;
                return result;

            }

            System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
            {
                return null;
            }

            void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
            {

                // for (int k = 0; k < 26; k++)
                {
                    value = new double[25];
                    reader.Read();
                    reader.Read();

                    if (reader.MoveToContent() == System.Xml.XmlNodeType.Element)
                        alphabet = reader.Name[0];
                    int i = 0;
                    reader.ReadStartElement();
                    //reader.Read();            
                    while (reader.MoveToNextAttribute())
                    {

                        value[i] = (double.Parse(reader.GetAttribute("Value" + i)) / 255.0) > 0.92 ? 1.0 : 0.0;
                        i++;
                    }
                    reader.Skip();
                    reader.Skip();
                    reader.Skip();
                    reader.Skip();

                }

            }

            void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)
            {
                writer.WriteStartElement("Character");
                writer.WriteStartElement(alphabet.ToString());
                writer.WriteStartElement("Values");
                for (int i = 0; i < value.Length; i++)
                    writer.WriteAttributeString("Value" + i.ToString(), value[i].ToString());
                writer.WriteEndElement();
                writer.WriteEndElement();
                writer.WriteEndElement();
            }

            public double[] CalculateAlphabetMatrix(System.Drawing.Bitmap image, Rectangle roi, bool scale = true)
            {
                int step = 5;
                double[] result = new double[25];
                int StartX = roi.X;
                int StartY = roi.Y;
                int XStep = (int)Math.Ceiling((double)roi.Width / step);
                int YStep = (int)Math.Ceiling((double)roi.Height / step);
                int AdjX = (step * XStep) - roi.Width;
                int AdjY = (step * YStep) - roi.Height;
                for (int i = StartY; i < YStep * step; i++)
                {
                    for (int j = StartX; j < XStep * step; j++)
                    {
                        //ARGB struct
                        System.Drawing.Color c;
                        if (j >= roi.Width || i >= roi.Height)
                            c = System.Drawing.Color.White; //set as (255,255,255)
                        else
                        {
                            c = image.GetPixel(j, i);
                        }
                        int X = j / XStep;
                        int Y = i / YStep;
                        //if (result[(step * Y) + X])
                        //{
                        //    result[(step * Y) + X] = 0;
                        //}
                        result[(step * Y) + X] += (c.R + c.B + c.G) / 3;
                    }
                }
                for (int i = 0; i < result.Length; i++)
                {
                    result[i] = result[i] / (XStep * YStep);
                    if (scale)
                        result[i] /= 255;
#if DEBUG
                    result[i] = result[i] > 0.92 ? 1.0 : 0.0;
#endif
                }
                value = result;
                return result;
            }
        }
        static void Main(string[] args)
        {
            if (args.Length < 1)
            {
                Console.WriteLine("usage: AlphabetsToMatrix DirectoryContainingImages");
                return;
            }
            try
            {
                DirectoryInfo dir = new DirectoryInfo(args[0]);
                List<CharacterData> characters = new List<CharacterData>(dir.GetFiles().Length);
                int i = 0;
                foreach (FileInfo file in dir.EnumerateFiles())
                {
                    bool isImage = false;
                    switch (file.Extension)
                    {
                        case ".jpg":
                        case ".bmp":
                        case ".png":
                            isImage = true;
                            break;
                    }
                    if (!isImage)
                        continue;
                    try
                    {
                        System.Drawing.Bitmap image = new System.Drawing.Bitmap(file.FullName);
                        CharacterData character = new CharacterData(image, file.Name[0]);
                        character.CalculateAlphabetMatrix(image, new Rectangle(0, 0, image.Width, image.Height), false);
                        characters.Add(character);
                    }
                    catch (IOException ex)
                    {
                        Console.WriteLine("a fatal I/O has occurred, press Y to continue.");
                        if (char.ToUpper(Console.ReadKey().KeyChar) != 'Y')
                            return;
                    }
                }
                try
                {
                    XmlSerializer XZ = new XmlSerializer(typeof(CharacterData[]));
                    TextWriter TW = new StreamWriter("Output.xml");
                    XZ.Serialize(TW, characters.ToArray());
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Could not write output file, you may not have write access to directory or the file with the name Output.xml already exists");
                }
            }
            catch (IOException ex)
            {
                Console.WriteLine("A Fatal error has occurred, check if the directory exists and you have access to its contents");
            }

            
        }
    }
}
